package com.lsl.rdf.basics;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import com.lsl.rdf.annotation.InjectDescByCode;
import com.lsl.rdf.annotation.InjectDescByVal;
import com.lsl.rdf.annotation.InjectEnumTarget;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 枚举描述侵入工具类
 * Created by lsl on 2021/4/26.
 */
public class EnumDescInjectUtil {

    private EnumDescInjectUtil() {
    }

    /**
     * 类于字段关系缓存map
     */
    private static final Map<Class<?>, List<Field>> invadeFieldCacheMap = new ConcurrentHashMap<>(128);

    /**
     * 侵入对象字段，通过枚举值获取枚举描述并将枚举描述设置的对于的字段中
     *
     * @param beInvadeObj 被侵入对象
     */
    public static void invadeFieldSetDesc(Object beInvadeObj) {
        if (Objects.isNull(beInvadeObj)) {
            return;
        }
        doInvadeFieldSetDesc(Collections.singletonList(beInvadeObj), false);
    }

    /**
     * 侵入对象字段，通过枚举值获取枚举描述并将枚举描述设置的对于的字段中
     *
     * @param beInvadeObjs 被侵入对象列表
     */
    public static void invadeFieldsSetDesc(List<Object> beInvadeObjs) {
        if (CollectionUtil.isEmpty(beInvadeObjs)) {
            return;
        }
        doInvadeFieldSetDesc(beInvadeObjs, false);
    }

    /**
     * 执行侵入
     */
    public static void doInvadeFieldSetDesc(List<Object> beInvadeObjs, boolean throwEx) {
        if (CollectionUtil.isEmpty(beInvadeObjs)) {
            return;
        }
        try {
            beInvadeObjs.forEach(obj -> {
                if (Objects.nonNull(obj)) {
                    Class<?> targetClazz = obj.getClass();
                    InjectEnumTarget injectEnumTarget = targetClazz.getAnnotation(InjectEnumTarget.class);

                    if (Objects.isNull(injectEnumTarget)) {
                        return;
                    }

                    List<Field> fields = findInvadeFields(targetClazz);
                    if (Objects.isNull(fields) || CollectionUtil.isEmpty(fields)) {
                        return;
                    }

                    fields.forEach(field -> {
                        Object fieldValue = ReflectUtil.getFieldValue(obj, field);
                        if (Objects.isNull(fieldValue)) {
                            return;
                        }

                        String targetDescFieldName;
                        String desc;

                        if (fieldValue instanceof Integer) {
                            InjectDescByVal targetFieldVal = field.getAnnotation(InjectDescByVal.class);
                            if (Objects.isNull(targetFieldVal)) {
                                return;
                            }
                            targetDescFieldName = targetFieldVal.targetFieldName();
                            Class<? extends BasicsEnum> enumClass = targetFieldVal.enumClass();
                            desc = BasicsEnum.getDescByVal(enumClass, (Integer) fieldValue);
                        } else if (fieldValue instanceof String) {
                            InjectDescByCode targetFieldCode = field.getAnnotation(InjectDescByCode.class);
                            if (Objects.isNull(targetFieldCode)) {
                                return;
                            }
                            targetDescFieldName = targetFieldCode.targetFieldName();
                            Class<? extends BasicsEnum> enumClass = targetFieldCode.enumClass();
                            desc = BasicsEnum.getDescByCode(enumClass, (String) fieldValue);
                        } else {
                            return;
                        }

                        if (Objects.isNull(desc)) {
                            return;
                        }

                        if ("".equals(targetDescFieldName)) {
                            targetDescFieldName = field.getName() + "Desc";
                        }

                        Field targetDescField = ReflectUtil.getField(obj.getClass(), targetDescFieldName);
                        if (Objects.isNull(targetDescField)) {
                            return;
                        }

                        ReflectUtil.setFieldValue(obj, targetDescFieldName, desc);
                    });
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            if (throwEx) {
                throw e;
            }
        }
    }

    /**
     * 查询对象中指定侵入的字段
     *
     * @param targetClazz 目标对象
     * @return 需要侵入字段列表
     */
    private static List<Field> findInvadeFields(Class<?> targetClazz) {
        if (Objects.isNull(targetClazz)) {
            return null;
        }

        List<Field> fieldCacheList = invadeFieldCacheMap.get(targetClazz);

        if (Objects.nonNull(fieldCacheList)) {
            return fieldCacheList;
        }

        Field[] fields = ReflectUtil.getFieldsDirectly(targetClazz, false);
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            InjectDescByVal injectDescByVal = field.getAnnotation(InjectDescByVal.class);
            InjectDescByCode injectDescByCode = field.getAnnotation(InjectDescByCode.class);
            if (Objects.isNull(injectDescByVal) && Objects.isNull(injectDescByCode)) {
                continue;
            }
            fieldList.add(field);
        }
        if (!fieldList.isEmpty()) {
            invadeFieldCacheMap.put(targetClazz, fieldList);
        }
        return fieldList;
    }
}
